iT邦幫忙

2022 iThome 鐵人賽

DAY 9
1
Modern Web

React Hook 不求人,建立自己的 Hook Libary系列 第 9

[DAY 09] 自己的Hook自己做!useToggle 再進化,useToggle+!

  • 分享至 

  • xImage
  •  

延伸情境

前一篇的 useToggle 顯然可以再更好,除了一般狀態之間切換,也可以再加入特定狀態的指定切換,來讓整體閱讀更直覺;也可以再嘗試額外加入 side-effect,來讓整體更多功能性。 (PLUS~ ULTRA~!)

功能與描述

我們需要:

  • 能切換到特定狀態
  • 能在特定狀態時額外觸發一些事件

開始!

能切換到特定狀態

繼承前一篇的內容,我們再加入 toggleOn & toggleOff

function useToggle(defaultState = false) {
  if(typeof defaultState !== 'boolean') {
    throw new Error("useToggle: defaultState should be Boolean")
  }
  
  const [state, setState] = useState(defaultState)

  const toggle = useCallback(() => setState((prev) => !prev),[])
  const toggleOn = useCallback(() => setState(true),[])
  const toggleOff = useCallback(() => setState(false),[])

  return {isOn: state, toggle, toggleOn, toggleOff}
}

現在我們更可以透過這個 Hook 來掌控元件,想要開就 toggleOn,想要關就 toggleOff。

return 的部分則改使用 object,東西一多,用 array 的話反而太卡手。

這次搭配了不同畫面一起實作:

function Example() {
  const [count, setCount] = useState(0)
  const { isOn, toggle, toggleOn, toggleOff } = useToggle()
  return (
    <>
      <Flex gap={2} sx={{ "& > *": { flex: 1 } }}>
        <Button onClick={toggle}>TOGGLE</Button>
        <Button onClick={toggleOn}>ON</Button>
        <Button onClick={toggleOff}>OFF</Button>
      </Flex>
      <Fade in={isOn}>
        <StyledBox>Hello (´・ω・`)</StyledBox>
      </Fade>
    </>
  )
}

實作上的 component 都從 Chakra-UI 而來,客官們請多注意 hook 的表演

成果如下:

想開就開,想關就關 d(`・∀・)b

能在特定狀態時額外觸發一些事件

切換狀態之餘,我們可能會想伴隨一些其他動作,通常會另外這樣包裝:

function handleOn () {
  toggleOn()
  //do other stuff
}

可以進一步把這個想法加入到 useToggle 裡面,我們需要:

  • hook 會傳入一個 object,包含 { defaultState, onOn, onOff } (分別為初始狀態、ture 時要觸發的 callback、以及 false 要觸發的 callback )
  • toggleOn / toggleOff 會額外執行 onOn / onOff (當有傳入的時候)
  • toggle 改藉由當前的 state 來判斷要執行 toggleOn or toggleOff

請原諒後來才發現取名 onOn,我承認很蠢

function useToggle(props = {}) {
  const { defaultState, onOn, onOff } = props

  if (defaultState !== undefined && typeof defaultState !== "boolean") {
    throw new Error("UseToggle: defaultState should be Boolean")
  }

  const [state, setState] = useState(defaultState || false)

  const toggleOn = useCallback(() => {
    setState(true)
    onOn?.()
  }, [])

  const toggleOff = useCallback(() => {
    setState(false)
    onOff?.()
  }, [])

  const toggle = useCallback(() => {
    const action = state ? toggleOff : toggleOn
    action()
  }, [state])

  return { isOn: state, toggle, toggleOn, toggleOff }
}

應用

假設我們想統計這個元件有幾次被嘗試打開:

const { isOn, toggle, toggleOn, toggleOff } = useToggle({
  onOn: () => setCount(count + 1),
})

這樣一來確實可以執行呢!

修但幾咧!怎麼好像怪怪的?

由於這邊使用了 useCallback,導致傳入的 onOn() 仍然是先前被記憶的 function,所以不管怎麼操作,執行上都會一直 0 + 1,但聰明的大家應該想到:

「那就都把 useCallback 移除吧!」

「如果要用 useCallback ,就把會改變的東西放到 [] 就好了啊」

「那就不要用這個方法唄~」

STOP!

既然是鐵人賽,讓我們再努力一下!

其實我們可以藉由 useRef + useEffect 來進一步實踐這個想法!

實際落成就留到明天再繼續吧!


上一篇
[DAY 08] 自己的Hook自己做!useToggle 讓你想開就開,想關就關
下一篇
[DAY 10] 自己的Hook自己做!useCallbackEvent 真的有必要嗎?
系列文
React Hook 不求人,建立自己的 Hook Libary30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言